package battleofthejavasea;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 *
 * @author Murata, Saito, Santana
 * Classe que efetua listening de ports e gerenciamento de conexões.
 * Módulo servidor integrado ao jogo.
 * Também é iniciado como uma Thread separada, assim que o jogador opta por
 * começar um servidor.
 *
 * Para comentários mais detalhados, ver ref. do módulo cliente.
 * Suas funcionalidades são análogas.
 */
public class BTJSServerInnerModule implements Runnable {

    private ServerSocket sktServer = null;
    private Socket connection = null;
    private ObjectInputStream in = null;
    private ObjectOutputStream out = null;
    private int listeningPort;
    //variável necessária para o servidor ter acesso ao contexto do jogo.
    private BTJSGameFrame btjsGF = null;
    //ArrayList para manter track de posições já c/ objetos
    private ArrayList<Integer> alRenderTrack = new ArrayList<Integer>();
    //Variável para manter mudanças de turno do jogo
    private boolean gameTurnChangeTracker;
    //Variável para manter track do número de mudanças realizadas pelas Threads nas variáveis de checagem de turno
    private int checkChanges = 0;

    public BTJSServerInnerModule(int listeningPort, BTJSGameFrame btjsGF){
        this.listeningPort = listeningPort;
        this.btjsGF = btjsGF;
        this.gameTurnChangeTracker = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn();
        
        //Servidor sempre começa os jogos;
        btjsGF.btjsGameLogic.getPlayer1BoatsCoords().setIsMyTurn(true);
    }

    @Override
    public void run(){
        try{
            //Cria novo server socket para aguardar conexões em uma porta determinada.
            sktServer = new ServerSocket(listeningPort);

            //Loop infinito para listening de conexões
            while(true){

                try {
                    Thread.currentThread().sleep(500);
                } catch (InterruptedException ex) {
                    Logger.getLogger(BTJSServerInnerModule.class.getName()).log(Level.SEVERE, null, ex);
                }

                listenConnection();
                getStreams();

                try {
                    manageConnection();
                } catch (ClassNotFoundException ex) {
                    ex.printStackTrace();
                }
            }
        }catch ( IOException excepcionES ) {
            excepcionES.printStackTrace();
        }
        finally{
            closeConnection();
        }
    }

    private void listenConnection() throws IOException{
        //Aguarda conexão
        connection = sktServer.accept();
    }

    private void getStreams() throws IOException{
        //Inicia fluxos de saída e entrada.
        out = new ObjectOutputStream( connection.getOutputStream() );
        out.flush();
        
        in = new ObjectInputStream( connection.getInputStream() );
    }

    //Gerencia a conexão, atualiza informações de variáveis de jogo.
    private void manageConnection() throws IOException, ClassNotFoundException
    {
        while(true){
            try{
            BTJS_DTOPlayerBoats obj = (BTJS_DTOPlayerBoats) btjsGF.btjsGameLogic.getPlayer1BoatsCoords();
            sendData(obj);

            BTJS_DTOPlayerBoats retrieve = (BTJS_DTOPlayerBoats)in.readObject();
            btjsGF.btjsGameLogic.setPlayer2Boats(retrieve);

            getEnemyShots();

            trackTurnChange();

            }
            catch(Exception ex){

            }
        }
     }


    //Fecha a conexão
    private void closeConnection(){
        try{
            out.close();
            in.close();
            connection.close();
        }
        catch(IOException exceptionIO){
            exceptionIO.printStackTrace();
        }
    }

    //Envia dados como Object.
    private void sendData(Object obj){
        try{
            out.writeObject(obj);
            out.flush();
            out.reset();
        }
        catch(IOException exceptionIO){
            exceptionIO.printStackTrace();
        }

    }

    private void getEnemyShots() throws FileNotFoundException{
        //Objeto para marcar tiros
        BTJSGameSymbols btjsGS = new BTJSGameSymbols();
        //Interpretador de posicionamento para a cena 3d
        BTJSPositionInterpreter btjsPositionInterpreter = new BTJSPositionInterpreter();

        if(btjsGF.btjsGameLogic.getPlayer2BoatsCoords().currentIndexEnemyShots != 0)
            for(int i = 0; i < 100; i ++)  {
                    //Se bup não for nulo
                    BTJS_DTOBoatUnitPosition bup = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getEnemyShotPositions()[i];
                        if(bup != null)
                        //Se foi atingida e ainda não possui símbolo renderizado, adiciona esfera vermelha em meu tabuleiro
                        if(bup.hasBeenShot && !alRenderTrack.contains(i)){
                            btjsGF.simpleUniverse.addBranchGraph((btjsGS.hitMe(btjsPositionInterpreter.getColumnValue(bup.column), btjsPositionInterpreter.getRowValue(bup.row))));
                            alRenderTrack.add(i);
                        }
                        //caso errou e também ainda não foi notificado, adiciona x vermelho em meu tabuleiro
                        else if(!bup.hasBeenShot && !alRenderTrack.contains(i)){
                            btjsGF.simpleUniverse.addBranchGraph((btjsGS.missedMe(btjsPositionInterpreter.getColumnValue(bup.column), btjsPositionInterpreter.getRowValue(bup.row))));
                            alRenderTrack.add(i);
                        }
            }
    }

    //Monitora troca de turnos
    private void trackTurnChange(){

        if(gameTurnChangeTracker != btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn()){
            checkChanges++;
            gameTurnChangeTracker = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn();
            //Ajuste em decorrência das alterações realizadas pelas Threads C/S
            if(checkChanges == 2){
                gameTurnChangeTracker = btjsGF.btjsGameLogic.getPlayer2BoatsCoords().getIsMyTurn();
                btjsGF.btjsGameLogic.getPlayer1BoatsCoords().setIsMyTurn(!btjsGF.btjsGameLogic.getPlayer1BoatsCoords().getIsMyTurn()) ;
                checkChanges = 0;
            }
        }
    }
}
